﻿using System;
using System.Collections.Generic;
using TradeHelp.MaterializedObjectViews.Helpers;
using TradeHelp.MaterializedObjectViews.Views;
using TradeHelp.MaterializedObjectViews.Views.Caches;
using TradeHelp.Utilities;

namespace TradeHelp.MaterializedObjectViews
{
    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    public class Query<TSource>
    {
        private readonly IViewStream<TSource> _viewStream;
        private Func<TSource, bool> _predicate;
        private IComparer<TSource> _comparer;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="viewViewStream"></param>
        public Query(IViewStream<TSource> viewViewStream)
        {
            Invariant.CheckIsNull(viewViewStream, "viewViewStream");
            _viewStream = viewViewStream;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="keySelector"></param>
        /// <returns></returns>
        public Query<TSource> OrderBy<TKey>(Func<TSource, TKey> keySelector)
        {
            Invariant.CheckIsNull(keySelector, "keySelector");
            _comparer = ComparerHelper.GetKeyComparer(keySelector);
            return this;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public Query<TSource> Where(Func<TSource, bool> predicate)
        {
            Invariant.CheckIsNull(predicate, "predicate");
            _predicate = predicate;
            return this;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="selector"></param>
        /// <returns></returns>
        public Query<TResult> Select<TResult>(Func<TSource, TResult> selector)
        {
            Invariant.CheckIsNull(selector, "selector");

            var pred = _predicate ?? new Func<TSource, bool>(source => true);
            Func<ViewMessageBurst<TSource>, ViewMessageBurst<TResult>> transformer = burst => burst.Where(pred).Select(selector);
            
            var stream = new ViewMsgStream<TSource, TResult>(_viewStream, transformer);
            return new Query<TResult>(new SelectQueryView<TResult>(stream, new ViewCache<TResult>()));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TInner"></typeparam>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="inner"></param>
        /// <param name="outerKeySelector"></param>
        /// <param name="innerKeySelector"></param>
        /// <param name="resultSelector"></param>
        /// <returns></returns>
        public Query<TResult> Join<TInner, TKey, TResult>(Query<TInner> inner, Func<TSource, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TSource, TInner, TResult> resultSelector)
        {
            Invariant.CheckIsNull(inner, "inner");
            Invariant.CheckIsNull(outerKeySelector, "outerKeySelector");
            Invariant.CheckIsNull(innerKeySelector, "innerKeySelector");
            Invariant.CheckIsNull(resultSelector, "resultSelector");

            return null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public QueryView<TSource> CreateView()
        {
            if (_viewStream is QueryView<TSource> && _predicate == null && _comparer == null)
            {
                return _viewStream as QueryView<TSource>;
            }

            Func<ViewMessageBurst<TSource>, ViewMessageBurst<TSource>> transformer = burst =>
            {
                if(_predicate != null)
                {
                    burst = burst.Where(_predicate);
                }
                return burst;
            };

            var stream = new ViewMsgStream<TSource, TSource>(_viewStream, transformer);
            var cache = _comparer == null ? (IViewCache<TSource>) new ViewCache<TSource>() : new OrderedViewCache<TSource>(_comparer);
            return new SelectQueryView<TSource>(stream, cache);
        }
    }
}